package com.tabbinder.client;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * The left panel containing the list of binders and tools for creating new
 * binders from the web UI.
 * 
 * According to the HTTP 1.1 specification, GET, HEAD, PUT and DELETE are
 * idempotent, while POST is not. So, for creation of a new binder we will use
 * POST (it takes a binder with id -1 as input). For updates we will use PUT.
 * Other than that, binder creation and update looks the same.
 * 
 * @author Tatiana Kichkaylo
 */
public class ListPanel {
	/** Reference to the master to be passed to list items */
	private MasterBind master;

	/** Request for getting all binders */
	private RequestBuilder getBuilder;
	/** Request for adding/modifying a binder */
	private RequestBuilder postBuilder;
	/** Request to delete an item */
	private RequestBuilder deleteBuilder;

	private VerticalPanel list;
	private ListItem selectedItem = null;
	private Label newButton;

	/* Various GUI string constants */
	private static final String NEW = "Add new";
	private static final String DEFAULT_ITEM_TITLE = "New binder";

	public ListPanel(final MasterBind master) {
		this.master = master;

		getBuilder = new RequestBuilder(RequestBuilder.GET,
				Constants.BINDER_URL);
		getBuilder.setCallback(new CommonResponseHandler() {
			@Override
			protected void processResponseText(String text) {
				parseJsonListData(text);
			}
		});
		postBuilder = new RequestBuilder(RequestBuilder.POST,
				Constants.BINDER_URL);
		postBuilder.setCallback(new CommonResponseHandler() {
			@Override
			protected void processResponseText(String text) {
				parseJsonItemData(text);
			}
		});
		// Work around server refusing to process HTTP DELETE
		// deleteBuilder = new UnsafeRequestBuilder("DELETE", Constants.BINDER_URL);
		deleteBuilder = new UnsafeRequestBuilder("POST", Constants.BINDER_URL + "?action=delete");
		deleteBuilder.setCallback(new CommonResponseHandler() {
			@Override
			protected void processResponseText(String text) {
				// Ignore the return, reload everything
				reloadList();
				master.itemSelected(null);
			}
		});

		list = new VerticalPanel();
		ScrollPanel listPanel = new ScrollPanel(list);
		RootPanel.get("listPanelBox").add(listPanel);

		RootPanel buttons = RootPanel.get("listButtons");

		newButton = new Label(NEW);
		buttons.add(newButton);
		newButton.addStyleName("binderButton");
		newButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				addNewItem();
			}
		});

		// Load data on startup
		reloadList();
	}

	protected void reloadList() {
		try {
			getBuilder.send();
		} catch (RequestException e) {
			Window.alert("Cannot send GET: " + e.toString());
		}
	}

	protected void parseJsonListData(String text) {
		JSONArray jsonList;
		try {
			jsonList = (JSONArray) JSONParser.parse(text);
		} catch (JSONException e) {
			Window.alert("Cannot parse response: " + e);
			return;
		}

		list.clear();
		for (int i = 0; i < jsonList.size(); i++) {
			JSONObject item = (JSONObject) jsonList.get(i);
			list.add(new ListItem(master, item));
		}
	}

	/**
	 * Create new Binder with default values, send it to the server. The
	 * callback will make the new item current selection
	 */
	protected void addNewItem() {
		// Create new object in JSON
		JSONObject item = new JSONObject();
		// Id == -1 means new
		item.put("id", new JSONNumber(-1));
		item.put("title", new JSONString(DEFAULT_ITEM_TITLE));

		// Send the object to the server. Back comes the updated records
		postBuilder.setRequestData(item.toString());
		try {
			postBuilder.send();
		} catch (RequestException e) {
			Window.alert("Cannot send POST: " + e.toString());
		}
	}

	protected void parseJsonItemData(String text) {
		try {
			JSONObject item = (JSONObject) JSONParser.parse(text);
			// If this is about the current item, ignore it
			if (selectedItem == null
					|| !selectedItem.getId().equals(item.get("id")))
				list.add(new ListItem(master, item));
		} catch (JSONException e) {
			Window.alert("Cannot parse response: " + e);
			return;
		}
	}

	public void selectItem(ListItem item) {
		if (selectedItem != null)
			selectedItem.removeStyleName("selectedItem");
		selectedItem = item;
		if (selectedItem != null)
			selectedItem.addStyleName("selectedItem");
	}

	public void submitUpdate(ListItem item) {
		postBuilder.setRequestData(item.getJson().toString());
		try {
			postBuilder.send();
		} catch (RequestException e) {
			Window.alert("Cannot send PUT: " + e.toString());
		}
	}

	public void removeItem(ListItem item) {
		// Send over only the ID
		deleteBuilder.setRequestData(item.getId().toString());
		try {
			deleteBuilder.send();
		} catch (RequestException e) {
			Window.alert("Cannot send DELETE: " + e.toString());
		}
	}
}
